//
//  CoinApi.m
//  Test
//
//  Created by xgc on 12/25/17.
//  Copyright © 2017 xgc. All rights reserved.
//

#import "CoinApi.h"
#import "../common/CommonCoinApi.hpp"
#import "CABtSeed.h"
#import "CANetParams.h"
#import "CABtWallet.h"
#import "BtcTransactionSignParams.h"
#import "EthTransactionSignParams.h"

@implementation CoinApi
{
    CommonCoinApi* cca;
}

+(id)sharedManager {
    static CoinApi* sharedMyManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedMyManager = [[self alloc] init];
    });
    return sharedMyManager;
}

-(id)init {
    if (self = [super init]) {
        self->cca = new CommonCoinApi();
    }
    return self;
}

-(NSMutableArray*) createAllCoinMnemonicCode {
    
    NSMutableArray* MneArray  = [[NSMutableArray alloc] init];
    
    if(self->cca) {
        vector<string> words;
        words = cca->createAllCoinMnemonicCode();
        
        int size = words.size();
        for(int i = 0; i < size; i++) {
            string str = words[i];
            
            NSString* nsStr = [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]];
            [MneArray addObject:nsStr];
        }
    }
    
    return MneArray;
}

-(BOOL) checkMnemonicCode:(NSArray*)words {
    vector<string> wl;
    
    for(id obj in words) {
        wl.push_back([obj UTF8String]);
    }
    
    if(self->cca) {
        bool ret = cca->checkMnemonicCode(wl);
        return ret ? YES : NO;
    }
    
    
    return NO;
}

-(NSString*) getPriKeyFromBtSeed:(CABtSeed*) btSeed withPassword:(NSString*) password withNetParams:(CANetParams*) netParams {
    
    BtSeed* seed = [btSeed toBtSeed];
    NetParams* params = [netParams toNetParams];
    
    string strPassword = [password UTF8String];
    
    string strPriKey;
    if(self->cca) {
        strPriKey = cca->getPriKeyFromBtSeed(*seed, strPassword, *params);
    }
    
    if(seed) {
        delete seed;
    }
    
    if(params) {
        delete params;
    }
    
    return [NSString stringWithCString:strPriKey.c_str() encoding:[NSString defaultCStringEncoding]];
}

-(NSMutableArray*) getMnemonicCodeFromBtSeed:(CABtSeed*) btSeed withPassword:(NSString*) password withNetParams:(CANetParams*) netParams
{
    BtSeed* seed = [btSeed toBtSeed];
    NetParams* params = [netParams toNetParams];
    string strPassword = [password UTF8String];
    
    vector<string> vMne;
    
    if(self->cca) {
        vMne = cca->getMnemonicCodeFromBtWallet(*seed, strPassword, *params);
    }
    
    if(seed) {
        delete seed;
    }
    
    if(params) {
        delete params;
    }
    
    NSMutableArray* MneArray  = [[NSMutableArray alloc] init];
    
    int size = vMne.size();
    for(int i = 0; i < size; i++) {
        string str = vMne[i];
        
        NSString* nsStr = [NSString stringWithCString:str.c_str() encoding:[NSString defaultCStringEncoding]];
        [MneArray addObject:nsStr];
    }
    
    return MneArray;
}

-(BOOL) validateAddress:(NSString*) address withNetParams:(CANetParams*) netParams
{
    NetParams* params = [netParams toNetParams];
    string strAddress = [address UTF8String];
    bool ret = false;
    
    if(self->cca) {
        ret = cca->validateAddress(strAddress, *params);
    }
    
    if(params) {
        delete params;
    }
    
    return ret ? YES : NO;
}

-(CABtSeed*) changePassword: (CABtSeed*) btSeed withOldPassword: (NSString*)oldPassword withNewPassword: (NSString*) newPassword withNetParams:(CANetParams*) netParams
{
    BtSeed* seed = [btSeed toBtSeed];
    NetParams* params = [netParams toNetParams];
    string strOldPassword = [oldPassword UTF8String];
    string strNewPassword = [newPassword UTF8String];
    bool ret = false;
    
    BtSeed newBtSeed;
    
    if(self->cca) {
        ret = cca->changePassword(*seed, strOldPassword, strNewPassword, newBtSeed, *params);
    }
    
    if(seed) {
        delete seed;
    }
    
    if(!ret) {
        return nil;
    }
    
    
    CABtSeed* newCABtSeed = [CABtSeed initWithBtSeed:&newBtSeed];
    
    return newCABtSeed;
}

-(CABtWallet*) createWallet: (NSString*) words withPassword: (NSString*)password withNetParams: (CANetParams*) netParams
{
    NetParams* params = [netParams toNetParams];
    string strWords = [words UTF8String];
    string strPassword = [password UTF8String];
    bool ret = false;
    
    BtWallet btWallet;
    
    if(self->cca) {
        ret = cca->createWallet(strWords, strPassword, *params, btWallet);
    }
    
    if(params) {
        delete params;
    }
    
    if(!ret) {
        return nil;
    }
    
    CABtWallet* caBtWallet = [CABtWallet initWithBtWallet: &btWallet];
    return caBtWallet;
}

-(NSMutableArray*) createWallets: (NSString*) words withPassword: (NSString*)password withNetParamList: (NSArray*) netParamList
{
    NSMutableArray* walletArray  = [[NSMutableArray alloc] init];
    
    for(id obj in netParamList) {
        CANetParams* caNetParams = (CANetParams*)obj;
        
        CABtWallet* caBtWallet = [self createWallet:words withPassword:password withNetParams:caNetParams];
        if(caBtWallet != nil) {
            [walletArray addObject:caBtWallet];
        }
    }
    
    return walletArray;
}

-(NSMutableData*) getTokenTransferBinary: (NSString*)address withValue: (NSString*)value
{
    NSMutableData* binData = nil;
    
    string strAddress = [address UTF8String];
    string strValue = [value UTF8String];
    
    u256 uValue;
    stringstream stream;
    stream << strValue;
    stream >> uValue;
    
    bool ret = false;
    vector<uint8_t> encode;
    
    if(self->cca) {
        ret = cca->getTokenTransferBinary(strAddress, uValue, encode);
    }
    
    if(ret) {
        binData = [[NSMutableData alloc]initWithBytes:&encode[0] length:encode.size()];
    }
    
    return binData;
}


-(NSMutableData*) getLbtcRegister: (NSString*) address withPrikey: (NSString*) prikey withName: (NSString*) name withNetParams: (CANetParams*) netParams
{
	NSMutableData* binData = nil;
	
	string strAddress = [address UTF8String];
	string strPrikey = [prikey UTF8String];
	string strName = [name UTF8String];
	
	NetParams* params = [netParams toNetParams];
	
	
	bool ret = false;
    vector<uint8_t> encode;
	
	if(self->cca) {
        ret = cca->getLbtcRegister(strAddress, strPrikey, strName, *params, encode);
    }
    
    if(ret) {
        binData = [[NSMutableData alloc]initWithBytes:&encode[0] length:encode.size()];
    }
	
	return binData;
}


-(NSMutableData*) getLbtcVote: (NSString*) address withPrikey: (NSString*) prikey withAddrList: (NSArray*) addrList withNetParams: (CANetParams*) netParams
{
	NSMutableData* binData = nil;
	
	string strAddress = [address UTF8String];
	string strPrikey = [prikey UTF8String];
	
	NetParams* params = [netParams toNetParams];
	
	vector<string> wl;
    
    for(id obj in addrList) {
        wl.push_back([obj UTF8String]);
    }
	
	bool ret = false;
    vector<uint8_t> encode;
	
	if(self->cca) {
        ret = cca->getLbtcVote(strAddress, strPrikey, wl, *params, encode);
    }
    
    if(ret) {
        binData = [[NSMutableData alloc]initWithBytes:&encode[0] length:encode.size()];
    }
	
	return binData;
}

-(NSMutableData*) getLbtcCancelVote: (NSString*) address withPrikey: (NSString*) prikey withAddrList: (NSArray*) addrList withNetParams: (CANetParams*) netParams
{
	NSMutableData* binData = nil;
	
	string strAddress = [address UTF8String];
	string strPrikey = [prikey UTF8String];
	
	NetParams* params = [netParams toNetParams];
	
	vector<string> wl;
    
    for(id obj in addrList) {
        wl.push_back([obj UTF8String]);
    }
	
	bool ret = false;
    vector<uint8_t> encode;
	
	if(self->cca) {
        ret = cca->getLbtcCancelVote(strAddress, strPrikey, wl, *params, encode);
    }
    
    if(ret) {
        binData = [[NSMutableData alloc]initWithBytes:&encode[0] length:encode.size()];
    }
	
	return binData;
}

-(NSMutableDictionary*) createSignTransaction: (TransactionSignParams*) signParams withNetParams: (CANetParams*) netParams
{
    NetParams* params = [netParams toNetParams];
    NSMutableDictionary* signDict = [[NSMutableDictionary alloc]init];
    
    map<string, string> resultMap;
    bool bOK = false;
    
    switch (params->getCoinType()) {
        case CoinType::BTC:
        case CoinType::LTC:
        case CoinType::DOGE:
        case CoinType::SBTC:
        case CoinType::WBTC:
        case CoinType::ZEC:
        case CoinType::DSH:
        case CoinType::BCH:
        case CoinType::QTUM:
        {
            Class classBtcTransactionSignParams = [BtcTransactionSignParams class];
            BOOL ret = [signParams isKindOfClass: classBtcTransactionSignParams];
            if(ret == NO) {
                break;
            }
            
            BtcTransactionSignParams* caCreateParams = (BtcTransactionSignParams*) signParams;
            
            BtcCreateSignTransactionParams* createParams = [caCreateParams toBtcCreateSignTransactionParams];
            
            if(self->cca) {
                bOK = cca->createSignTransaction(createParams, *params, resultMap);
            }
            
            delete createParams;
            
        }
        break;
        case CoinType::ETH:
        case CoinType::ETC:
        {
                Class classEthTransactionSignParams = [EthTransactionSignParams class];
                BOOL ret = [signParams isKindOfClass: classEthTransactionSignParams];
                if(ret == NO) {
                    break;
                }
                
                EthTransactionSignParams* caCreateParams = (EthTransactionSignParams*) signParams;
                
                EthCreateSignTransactionParams* createParams = [caCreateParams toEthCreateSignTransactionParams];
                
                if(self->cca) {
                    bOK = cca->createSignTransaction(createParams, *params, resultMap);
                }
                
                delete createParams;
        }
        break;
    }
    
    delete params;
    
    if(bOK) {
        map<string, string>::iterator it;
        it = resultMap.find("hex");
        if(it != resultMap.end()){
            string strHex = it->second;
            NSString* nsHex = [NSString stringWithCString:strHex.c_str() encoding:[NSString defaultCStringEncoding]];
            [signDict setValue:nsHex forKey:@"hex"];
        }
        

        
        it = resultMap.find("length");
        if(it != resultMap.end()){
            string strLength = it->second;
            NSString* nsLength = [NSString stringWithCString:strLength.c_str() encoding:[NSString defaultCStringEncoding]];
            [signDict setValue:nsLength forKey:@"length"];
        }

        
        it = resultMap.find("fee");
        if(it != resultMap.end()){
            
            string strFee = it->second;
            NSString* nsFee = [NSString stringWithCString:strFee.c_str() encoding:[NSString defaultCStringEncoding]];
            [signDict setValue:nsFee forKey:@"fee"];
        }

    }

    
    return signDict;
}

- (void)dealloc {
    // Should never be called,but just here for clarity really.
    if(self->cca) {
        delete self->cca;
    }
}

@end
